/* 
      %Z%%M% %I% %E% %U%

***************************************************************
"Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

-Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

-Redistribution in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

Neither the name of Sun Microsystems, Inc. or the names of contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

This software is provided "AS IS," without a warranty of any kind. ALL
EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

You acknowledge that Software is not designed,licensed or intended for use in
the design, construction, operation or maintenance of any nuclear facility."

****************************************************************************
*/

package geometry3D;

import context.*;

import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.Font;

public class CoordSys extends Switch implements Context.Constants
{ //private static final boolean DEBUG=true; // Overriding Context.Constants.DEBUG


    // Temporaries that are reused
    Transform3D		tmpTrans = new Transform3D();
    Vector3f		tmpVector = new Vector3f();
    AxisAngle4f		tmpAxisAngle = new AxisAngle4f();

    // colors for use in the shapes
    Color3f 		black = new Color3f(0.0f, 0.0f, 0.0f);
    Color3f 		grey  = new Color3f(0.3f, 0.3f, 0.3f);
    Color3f 		white = new Color3f(1.0f, 1.0f, 1.0f);

    // geometric constants
    Point3f		origin = new Point3f();
    Vector3f		yAxis = new Vector3f(0.0f, 1.0f, 0.0f);

    public CoordSys(float axisLength) {
	super(Switch.CHILD_ALL);

	float coordSysLength = axisLength;
	float labelOffset = axisLength / 20.0f;
	float axisRadius  = axisLength / 500.0f;
	float arrowRadius = axisLength / 125.0f;
	float arrowHeight = axisLength / 50.0f;
	float tickRadius  = axisLength / 125.0f;
	float tickHeight  = axisLength / 250.0f;

	// Set the Switch to allow changes
	setCapability(Switch.ALLOW_SWITCH_READ);
	setCapability(Switch.ALLOW_SWITCH_WRITE);

	// Set up an appearance to make the Axis have 
	// grey ambient, black emmissive, grey diffuse and grey specular 
	// coloring.  
	//Material material = new Material(grey, black, grey, white, 64);
	Material material = new Material(white, black, white, white, 64);
	Appearance appearance = new Appearance();
	appearance.setMaterial(material);

	// Create a shared group to hold one axis of the coord sys
	SharedGroup coordAxisSG = new SharedGroup();

	// create a cylinder for the central line of the axis
	Cylinder cylinder = new Cylinder(axisRadius, coordSysLength, 
								appearance); 
	// cylinder goes from -coordSysLength/2 to coordSysLength in y
	coordAxisSG.addChild(cylinder);

	// create the shared arrowhead 
	Cone arrowHead = new Cone(arrowRadius, arrowHeight, appearance); 
	SharedGroup arrowHeadSG = new SharedGroup();
	arrowHeadSG.addChild(arrowHead);


	// Create a TransformGroup to move the arrowhead to the top of the 
	// axis
	// The arrowhead goes from -arrowHeight/2 to arrowHeight/2 in y. 
	// Put it at the top of the axis, coordSysLength / 2 
	tmpVector.set(0.0f, coordSysLength / 2 + arrowHeight/2, 0.0f);
	tmpTrans.set(tmpVector);
	TransformGroup topTG = new TransformGroup();	
	topTG.setTransform(tmpTrans);
	topTG.addChild(new Link(arrowHeadSG));
	coordAxisSG.addChild(topTG);

	// create the minus arrowhead
	// Create a TransformGroup to turn the cone upside down:
	// Rotate 180 degrees around Z axis
	tmpAxisAngle.set(0.0f, 0.0f, 1.0f, (float)Math.toRadians(180)); 
	tmpTrans.set(tmpAxisAngle);
	
	// Put the arrowhead at the bottom of the axis 
	tmpVector.set(0.0f, -coordSysLength/2 - arrowHeight/2, 0.0f);
	tmpTrans.setTranslation(tmpVector);
	TransformGroup bottomTG = new TransformGroup();	
	bottomTG.setTransform(tmpTrans);
	bottomTG.addChild(new Link(arrowHeadSG));
	coordAxisSG.addChild(bottomTG);

	// Now add "ticks" at 1, 2, 3, etc.

	// create a shared group for the tick
	Cylinder tick = new Cylinder(tickRadius, tickHeight, appearance);
	SharedGroup tickSG = new SharedGroup();
	tickSG.addChild(tick);

	// transform each instance and add it to the coord axis group
	int maxTick = (int)(coordSysLength / 2);
	int minTick = -maxTick;
	for (int i = minTick; i <= maxTick; i++) {
	    if (i == 0) continue; // no tick at 0

	    // use a TransformGroup to offset to the tick location 
	    TransformGroup tickTG = new TransformGroup();
	    tmpVector.set(0.0f, (float) i, 0.0f);
	    tmpTrans.set(tmpVector);
	    tickTG.setTransform(tmpTrans);
	    // then link to an instance of the Tick shared group
	    tickTG.addChild(new Link(tickSG));
	    // add the TransformGroup to the coord axis
	    coordAxisSG.addChild(tickTG);
	}

	// add a Link to the axis SharedGroup to the coordSys
	addChild(new Link(coordAxisSG)); // Y axis

	// Create TransformGroups for the X and Z axes 
	TransformGroup xAxisTG = new TransformGroup();
	// rotate 90 degrees around Z axis
	tmpAxisAngle.set(0.0f, 0.0f, 1.0f, (float) Math.toRadians(90));
	tmpTrans.set(tmpAxisAngle);
	xAxisTG.setTransform(tmpTrans);
	xAxisTG.addChild(new Link(coordAxisSG)); 
	addChild(xAxisTG);		// X axis

	TransformGroup zAxisTG = new TransformGroup();
	// rotate 90 degrees around X axis
	tmpAxisAngle.set(1.0f, 0.0f, 0.0f, (float) Math.toRadians(90));
	tmpTrans.set(tmpAxisAngle);
	zAxisTG.setTransform(tmpTrans);
	zAxisTG.addChild(new Link(coordAxisSG)); 
	addChild(zAxisTG); 		// Z axis

	// Add the labels.  First we need a Font3D for the Text3Ds
	// select the default font, plain style, 0.5 tall.  Use null for
	// the extrusion so we get "flat" text since we will be putting it
	// into an oriented Shape3D
	Font3D f3d = new Font3D(new Font("Default", Font.PLAIN, 1), null);

	// set up the +X label
	Text3D plusXText = new Text3D(f3d, "+X", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D plusXTextShape = new OrientedShape3D(plusXText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup plusXTG = new TransformGroup();
	tmpVector.set(coordSysLength / 2 + labelOffset, 0.0f, 0.0f);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	plusXTG.setTransform(tmpTrans);
	plusXTG.addChild(plusXTextShape);
	addChild(plusXTG);

	// set up the -X label
	Text3D minusXText = new Text3D(f3d, "-X", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D minusXTextShape = new OrientedShape3D(minusXText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup minusXTG = new TransformGroup();
	tmpVector.set(-coordSysLength / 2 - labelOffset, 0.0f, 0.0f);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	minusXTG.setTransform(tmpTrans);
	minusXTG.addChild(minusXTextShape);
	addChild(minusXTG);

	// set up the +Y label
	Text3D plusYText = new Text3D(f3d, "+Y", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D plusYTextShape = new OrientedShape3D(plusYText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup plusYTG = new TransformGroup();
	tmpVector.set(0.0f, coordSysLength / 2 + labelOffset, 0.0f);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	plusYTG.setTransform(tmpTrans);
	plusYTG.addChild(plusYTextShape);
	addChild(plusYTG);

	// set up the -Y label
	Text3D minusYText = new Text3D(f3d, "-Y", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D minusYTextShape = new OrientedShape3D(minusYText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup minusYTG = new TransformGroup();
	tmpVector.set(0.0f, -coordSysLength / 2 - labelOffset, 0.0f);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	minusYTG.setTransform(tmpTrans);
	minusYTG.addChild(minusYTextShape);
	addChild(minusYTG);

	// set up the +Z label
	Text3D plusZText = new Text3D(f3d, "+Z", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D plusZTextShape = new OrientedShape3D(plusZText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup plusZTG = new TransformGroup();
	tmpVector.set(0.0f, 0.0f, coordSysLength / 2 + labelOffset);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	plusZTG.setTransform(tmpTrans);
	plusZTG.addChild(plusZTextShape);
	addChild(plusZTG);

	// set up the -Z label
	Text3D minusZText = new Text3D(f3d, "-Z", origin, Text3D.ALIGN_CENTER,
		Text3D.PATH_RIGHT);
	// orient around the local origin
	OrientedShape3D minusZTextShape = new OrientedShape3D(minusZText, 
		appearance, OrientedShape3D.ROTATE_ABOUT_POINT, origin);
	// transform to scale down to 0.15 in height, locate at end of axis
	TransformGroup minusZTG = new TransformGroup();
	tmpVector.set(0.0f, 0.0f, -coordSysLength / 2 - labelOffset);
//    tmpTrans.set(0.15f, tmpVector);
	tmpTrans.set(0.60f, tmpVector);
	minusZTG.setTransform(tmpTrans);
	minusZTG.addChild(minusZTextShape);
	addChild(minusZTG);
    }
}
